/** for SpaceTraderTest */
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.junit.Before;
import org.junit.Test;

/**
 * This is Junit Test class
 * @author CodeTraders
 * @version 1.0
 */
public class SpaceTraderTest {
	
	/**
	 * Array holding the arguments for the MainGUI
	 */
	private final String[] arg = new String[Numbers.SIX.getNumber()];
	
	/**
	 * Current SpaceTraderMainGUI
	 */
	private SpaceTraderMainGUI mainGUI;
	
	/**
	 * Current MarketPlace
	 */
	private MarketPlace market;
	
	/**
	 * Current Planet
	 */
	private Planet planet;
	
	/**
	 * Current Ship
	 */
	private Ship ship;

	/** 
	 * This is to setup our test.
	 * @throws Exception
	 */
	@Before
	public void init() {
		arg[0] = "Player";
		arg[1] = "6";
		arg[Numbers.TWO.getNumber()] = "5";
		arg[Numbers.THREE.getNumber()] = "3";
		arg[Numbers.FOUR.getNumber()] = "2";
		arg[Numbers.FIVE.getNumber()] = "3";
		mainGUI = new SpaceTraderMainGUI(arg, false);
	}

	/**
	 * Test method for the save and the load functions
	 *
	 * @throws IOException IOException for the save function
	 * @throws ClassNotFoundException ClassNotFoundException for the load function
	 */
	@SuppressWarnings("unchecked")
	@Test
	public void saveAndLoadTest() throws IOException, ClassNotFoundException {
		
		// modify the ship's information
		final Ship ship = (Ship) mainGUI.getSingleton().get("ship");
		ship.changeShip(Ships.BUMBLEBEE);
		if (ship.canAddShield(Shield.ENERGY)) {
			System.out.print("");
		}
		ship.subtractFuel(Numbers.TWO.getNumber());
		if (ship.canAddGadget(Gadget.NAVIGATION)) {
			System.out.print("");
		}
		ship.addGoods(Goods.FIREARMS, Numbers.THREE.getNumber());
		ship.addGoods(Goods.FURS, Numbers.TWO.getNumber());
		ship.addMoney(Numbers.THIRTY.getNumber());
		if (ship.canAddWeapon(Weapon.MILITARY)) {
			System.out.print("");
		}
		
		// save the current singleton instance to a map
		final Map<String, Object> testMap_0 =
				new HashMap<String, Object>(mainGUI.getSingleton());
		
		// save the current game state
		mainGUI.save(new File("testSaveFile"));
		
		// check that the test save file has been created
		assertTrue("Save file not created",
				new File("testSaveFile.codetraders").exists());
		
		// start a new game and load the saved test file
		mainGUI = new SpaceTraderMainGUI(arg, false);
		mainGUI.load(new File("testSaveFile.codetraders"));
		
		// save the loaded singleton instance to a map
		final Map<String, Object> testMap_1 =
				new HashMap<String, Object>(mainGUI.getSingleton());
		
		// check that the keys are the same
		final Set<String> keys_0 = testMap_0.keySet();
		final Set<String> keys_1 = testMap_1.keySet();
		assertEquals("Keys of the singletons are not the same", keys_0, keys_1);
		
		// check that the planet lists are the same
		final List<Planet> planets_0  = (ArrayList<Planet>) testMap_0.get("planets");
		final List<Planet> planets_1 = (ArrayList<Planet>) testMap_1.get("planets");
//		Collections.sort(planets_0);
//		Collections.sort(planets_1);
		assertArrayEquals("Planet lists are" +
				" not the same", planets_0.toArray(), planets_1.toArray());
		assertTrue("Planet lists are not the same", planets_0.equals(planets_1));
		
		// check that the players have the same values
		final Player player_0 = (Player) testMap_0.get("player");
		final Player player_1 = (Player) testMap_1.get("player");
		assertEquals("Player's names are" +
				" not the same", player_0.getName(), player_1.getName());
		assertEquals("Player's engineer skill" +
				" points are not the same",
				player_0.getEngineer(), player_1.getEngineer());
		assertEquals("Player's fighter skill" +
				" points are not the same",
				player_0.getFighter(), player_1.getFighter());
		assertEquals("Player's pilot skill" +
				" points are not the same",
				player_0.getPilot(), player_1.getPilot());
		assertEquals("Player's trader skill" +
				" points are not the same",
				player_0.getTrader(), player_1.getTrader());
		assertEquals("Player's points are not" +
				" the same", player_0.getPoint(), player_1.getPoint());
		
		// check that the ships have the same values
		final Ship ship_0 = (Ship) testMap_0.get("ship");
		final Ship ship_1 = (Ship) testMap_1.get("ship");
		assertTrue("Ships are not the same", ship_0.equals(ship_1));
		
		// check that the difficulties are the same
		assertEquals("Difficulty values are not" +
				" the same", testMap_0.get("difficulty"), testMap_1.get("difficulty"));
		
		// check that the universes have the same list of planets
		assertTrue("The planet list of the" +
				" universes are not the same",
				((Universe) testMap_0.get("universe")).getPlanets().equals
				(((Universe) testMap_1.get("universe")).getPlanets()));
		
		// delete the temporary test save file at the end
		if (new File("testSaveFile.codetraders").delete()) {
			System.out.print("");
		}
	}
	
	/** when cargo space is full**/
	@Test
	public void marketPlaceBuyTest1() {
		planet = mainGUI.getPlanet();
		ship = mainGUI.getShip();
		market = new MarketPlace();
		ship.setCargoSpace(Numbers.FIFTEEN.getNumber()); //set full cargo space
		assertFalse("CargoSpace: " +
		ship.getCargoSpace() +
		"There is no space to add more items",
		market.canBuy(Goods.FOOD, 1));
	}
	
	/** check tech level when user buys item **/
	@Test
	public void marketPlaceBuyTest2() {
		planet = mainGUI.getPlanet();
		ship = mainGUI.getShip();
		market = new MarketPlace();
		ship.setMoney(Numbers.TEN_THOUSAND.getNumber());
		if(market.getGoods()[Goods.FIREARMS.ordinal()].isAvailableToBuy()) {
			assertTrue("Tech level is proper to buy FIREARMS",
					market.canBuy(Goods.FIREARMS, 1));
		} else {
			assertFalse("Tech level is low to buy FIREARMS",
					market.canBuy(Goods.FIREARMS, 1));
		}
	}
	
	/** when user request to buy more than amount **/
	@Test
	public void marketPlaceBuyTest3() {
		planet = mainGUI.getPlanet();
		ship = mainGUI.getShip();
		market = new MarketPlace();
		ship.setMoney(Numbers.TEN_THOUSAND.getNumber());
		final Random rand = new Random();
		final int num = rand.nextInt(Numbers.FIFTY.getNumber());
		if(market.getGoods()[Goods.FOOD.ordinal()].getAmount() < num) {
			assertFalse("user request to buy FOOD more than" +
					" the totla amount", market.canBuy(Goods.FOOD, num));
		} 
	}
	
	/** when user doesn't have enough money to pay it **/
	@Test
	public void marketPlaceBuyTest4() {
		planet = mainGUI.getPlanet();
		ship = mainGUI.getShip();
		market = new MarketPlace();
		ship.setMoney(0);
		final int num = Numbers.FIVE.getNumber();
		if(ship.getMoney() < market.getGoods()[Goods.FOOD.ordinal()].getPrice() * num) {
			assertFalse("user does not have enought money" +
					" to pay it", market.canBuy(Goods.FOOD, num));
		}
	}
	
	/** to check decrease money  **/
	@Test
	public void marketPlaceBuyTest5() {
		planet = mainGUI.getPlanet();
		ship = mainGUI.getShip();
		market = new MarketPlace();
		ship.setMoney(Numbers.TEN_THOUSAND.getNumber());
		final int num = Numbers.FIVE.getNumber();
		final int money = ship.getMoney();
		final int item_money = market.getGoods()[Goods.FOOD.ordinal()].getPrice();
		if(market.canBuy(Goods.FOOD, num)) {
			assertEquals("Ship does not have right rest", 
					ship.getMoney(), money - (item_money * num));
		} else {
			assertEquals("Ship has to have 10000", ship.getMoney(), 
					Numbers.TEN_THOUSAND.getNumber());
		}
	}

	/**
	 * When user buy some goods, and sell these things again
	 * , the amount of the current ship should be same. 
	 */
	@Test
	public void marketPlaceSellTest1(){
		planet = mainGUI.getPlanet();
		ship = mainGUI.getShip();
		market = new MarketPlace();
		if (market.canBuy(Goods.WATER, Numbers.FIFTEEN.getNumber()) 
				&& market.canSell(Goods.WATER, Numbers.FIFTEEN.getNumber())) {
			System.out.print("");
		}
		assertEquals("Ship failed to sell", ship.getGoods()[Goods.WATER.ordinal()], 0);
	}
	
	/**
	 * To check user's money when they sell. 
	 */
	@Test
	public void marketPlaceSellTest2(){
		planet = mainGUI.getPlanet();
		ship = mainGUI.getShip();
		market = new MarketPlace();
		ship.setMoney(Numbers.FOUR_THOUSAND.getNumber());
		final int money = Numbers.FOUR_THOUSAND.getNumber();
		ship.addGoods(Goods.WATER, Numbers.FIFTEEN.getNumber());
		if (market.canSell(Goods.WATER, Numbers.FIFTEEN.getNumber())) {
			System.out.print("");
		}
		final int item_money = market.getGoods()[Goods.WATER.ordinal()].getPrice();
		assertEquals("Ship failed to sell", ship.getMoney(), 
				money + (item_money * Numbers.FIFTEEN.getNumber()));
	}
	
	/**
	 * To check user try to sell his item more than he has.
	 */
	@Test
	public void marketPlaceSellTest3(){
		planet = mainGUI.getPlanet();
		ship = mainGUI.getShip();
		market = new MarketPlace();
		ship.setMoney(Numbers.FIVE_THOUSAND.getNumber());
		if (market.canBuy(Goods.FOOD, Numbers.TEN.getNumber())) {
			System.out.print("");
		}
		assertFalse("Market failed to sell", 
				market.canSell(Goods.FOOD, Numbers.FIFTEEN.getNumber()));
	}
	
	/**
	 * To check when user sold his items, the market place
	 *  should hold more than its items.
	 */
	@Test
	public void marketPlaceSellTest4(){
		planet = mainGUI.getPlanet();
		ship = mainGUI.getShip();
		market = new MarketPlace();
		final int amt = market.getGoods()[Goods.WATER.ordinal()].getAmount();
		ship.addGoods(Goods.WATER, Numbers.FIFTEEN.getNumber());
		if (market.canSell(Goods.WATER, Numbers.FIFTEEN.getNumber())) {
			System.out.print("");
		}
		assertEquals("Market failed to sell", 
				market.getGoods()[Goods.WATER.ordinal()].getAmount(), 
				amt + Numbers.FIFTEEN.getNumber());
	}

	/**
	 * This is to string method.
	 * @return tostring
	 */
	@Override
	public String toString() {
		return super.toString();
	}
}
